feat: Azure OpenAI provider using @ai-sdk/azure#11144
feat: Azure OpenAI provider using @ai-sdk/azure#11144roomote[bot] wants to merge 20 commits intomainfrom
Conversation
All previously flagged issues are resolved. No new issues found. The Azure OpenAI provider implementation correctly follows the established AI SDK migration pattern, has comprehensive test coverage, and complete UI integration.
Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues. |
Review complete. No issues found. The Azure OpenAI provider implementation correctly follows the established AI SDK migration pattern (DeepSeek, Groq, Fireworks). The handler properly integrates with
Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues. |
25d2476 to
1c3437f
Compare
d00536b to
e30e3c1
Compare
webview-ui/src/components/settings/utils/providerModelConfig.ts
Outdated
Show resolved
Hide resolved
421699d to
8c31b80
Compare
There was a problem hiding this comment.
PR Review: feat(azure): Azure OpenAI provider with model metadata and model picker
Summary
Solid, well-structured PR that adds Azure OpenAI as a first-class provider. The implementation follows established patterns from other AI SDK providers (DeepSeek, Cerebras, etc.), with proper separation between types, API handler, and UI components. All tests pass (15 Azure handler tests, 13 parseAzureUrl tests, validate + checkExistApiConfig tests all green), and TypeScript compiles without errors.
What works well
- Pattern consistency:
AzureHandlermirrors the existingDeepSeekHandlerstructure almost 1:1 (imports, constructor, getModel, getLanguageModel, processUsageMetrics, getMaxOutputTokens, createMessage, completePrompt, isAiSdkProvider). Easy to review because the pattern is familiar. - Deployment vs. model separation: Clean design where
azureDeploymentNameis the API routing key andapiModelIdis the capability lookup key. The fallback chain ingetModel()(azureModels[modelId] -> azureModels[deploymentId] -> azureDefaultModelInfo) handles the case where the deployment name matches a known model. - URL auto-parser: Clean regex-based parser that extracts baseUrl and deploymentName from a pasted full Azure URL. Intentionally ignoring api-version to use the app default is a good design choice.
- Validation UX: Skipping errors on fresh provider selection (all fields empty) prevents annoying immediate errors when switching providers.
- Comprehensive localization: All 18 locales updated consistently.
- Test coverage: Good coverage of handler constructor, streaming, tool calls, errors, cache metrics via providerMetadata, and the URL parser edge cases.
Minor suggestions (non-blocking)
-
azureApiKeyempty string handling (src/api/providers/azure.ts:45): ThebaseURLparameter usesoptions.azureBaseUrl || undefinedto convert empty string to undefined, butapiKeydoes not. IfazureApiKeyis"", it would be passed through as-is. ConsiderapiKey: options.azureApiKey || undefinedfor symmetry, so the SDK can properly fall back to env vars when no key is provided. -
checkExistKeyloose check (src/shared/checkExistApiConfig.ts:12):!!(config.azureBaseUrl || config.azureDeploymentName || config.azureApiKey)means setting only a deployment name (without a base URL) returns true ("configured"), even though it cannot make API calls. The downstream validation invalidateModelsAndKeysProvidedcatches this, but it is worth noting thatcheckExistKeyis used in other paths too (e.g., onboarding flow). -
getModel()called multiple times per request: IncreateMessage,getModel()runs via direct call (line 130),getLanguageModel()(line 131 -> 75), andgetMaxOutputTokens()(line 146 -> 116). That is 3 invocations per streaming request, each re-computing model params. This is consistent with other providers (DeepSeek does the same), but if perf ever matters, caching the result per-request would be trivial. -
Test:
useDeploymentBasedUrlsnot asserted: ThecreateAzuremock does not verify thatuseDeploymentBasedUrls: trueis passed in the constructor. Since this is a critical behavioral flag (it changes the URL routing), adding an assertion likeexpect(createAzure).toHaveBeenCalledWith(expect.objectContaining({ useDeploymentBasedUrls: true }))would be a nice addition.
Verdict
LGTM -- clean implementation that follows project patterns, comprehensive test coverage, and proper type integration. The suggestions above are all minor and non-blocking.
- Add azureApiKey to SECRET_STATE_KEYS for proper configuration detection - Add Azure validation case in validateModelsAndKeysProvided - Add validation translations for azureResourceName and azureDeploymentName across all 18 locales This fixes the issue where the Finish button does nothing when setting up Azure provider in the onboarding workflow.
…ndry - Add static model metadata for 29 Azure models (from models.dev) with Roo-specific flags (reasoning, tools, verbosity) matching openAiNativeModels - Add model picker dropdown to Azure provider settings for model capability detection (context window, max tokens, pricing) - Rename provider label from 'Azure OpenAI' to 'Azure AI Foundry' across all 18 locales - Make API key optional (supports Azure managed identity / Entra ID) - Update default API version from 2024-08-01-preview to 2025-04-01-preview - Fix maxOutputTokens validation (filter invalid values <= 0) - Handler separates deployment name (API calls) from model ID (capability lookup) with azureDefaultModelInfo (gpt-4o) fallback - Remove unhelpful 'Get Azure AI Foundry Access' button - Prevent stale model IDs from other providers carrying over - Suppress validation errors on fresh provider selection
- Upgrade @ai-sdk/azure from ^2.0.6 to ^3.0.26
- v3 defaults provider(id) to Responses API (works for all models)
- v2 defaulted to Chat Completions, breaking codex models
- Add useDeploymentBasedUrls: true to createAzure()
- Produces /deployments/{id}/{path} URLs (universally compatible)
- Without it, SDK uses /v1/{path} which only works on AI Foundry resources
Expand azure model list from 27 (OpenAI-only) to 64 models with tool_call support, including Claude, Cohere, DeepSeek, Grok, Kimi, Llama, Mistral, Phi, and Model Router families. All model metadata (pricing, context window, capabilities) sourced from https://models.dev/api.json. Existing Roo-specific overrides (includedTools, excludedTools, reasoningEffort, etc.) preserved.
Remove gpt-4 (8k) and gpt-4-32k (32k) — too small for agentic use. 62 models remain, all with tool_call support and ≥128k context.
- Fix baseURL empty string fallback (pass undefined instead of "" to let SDK use env vars) - Add parseAzureUrl() utility that extracts endpoint, deployment name, and API version from a full Azure deployment URL - Integrate auto-parser into Azure settings: pasting a full URL auto-fills all fields - Rename "Base URL" to "Azure Endpoint" to match Azure portal terminology - Improve field descriptions for deployment name and API version - Add 13 tests for URL parser (all passing)
Pasted URLs often contain older API versions (e.g., 2024-05-01-preview) that are incompatible with the Responses API. The parser now only extracts endpoint and deployment name, letting the API version default to 2025-04-01-preview.
The API version field is an advanced option that 99% of users won't change. Hide it in the onboarding Welcome view using the existing simplifySettings pattern (matches OpenRouter's approach for custom base URL).
Update default Azure model from gpt-4o to gpt-5.2 and ensure reasoning effort defaults are properly configured.
Models with supportsReasoningEffort as an array (e.g., gpt-5.2) and no requiredReasoningEffort would show a blank dropdown because the default fell to 'disable' which wasn't in the available options. Now uses the model's declared reasoningEffort as the default.
- Handler: createAzure constructor args, dual-ID getModel lookup, processUsageMetrics, getMaxOutputTokens, reasoning events, tool-call ignoring, completePrompt error propagation, provider metadata key mismatch - Validation: 4 Azure branches in validate.spec.ts - Config: 4 Azure cases in checkExistApiConfig.spec.ts - Types: 6 model definition correctness tests, 3 Zod schema round-trips - URL parser: 3 additional edge cases Total: 105 tests across 6 suites (was 70)
2026ae7 to
d7be2b9
Compare
Summary
New Azure OpenAI provider built on
@ai-sdk/azurev3.x (AI SDK v6). Replaces the legacy Azure path in the OpenAI handler with a dedicated provider using the modern Vercel AI SDK.Provider Architecture
createAzure()from@ai-sdk/azurewithuseDeploymentBasedUrls: truefor Azure deployment-scoped endpoints2025-04-01-preview(required for Responses API support)openai.azure.com,cognitiveservices.azure.com, andservices.ai.azure.comendpointsSettings UX
2025-04-01-previewgpt-5.2withmediumreasoning effortURL Auto-Parser
Users can paste a full Azure deployment URL like:
The endpoint field auto-extracts the base URL and deployment name. The API version from the URL is intentionally ignored (the default is always correct for the Responses API).
Bug Fixes (shared components)
supportsReasoningEffort(affects all providers with gpt-5.x models)Test Coverage
parseAzureUrl.spec.ts)azure.spec.ts)Files
New
webview-ui/src/components/settings/utils/parseAzureUrl.tswebview-ui/src/components/settings/utils/__tests__/parseAzureUrl.spec.tsModified
src/api/providers/azure.ts— Azure handler using@ai-sdk/azurewebview-ui/src/components/settings/providers/Azure.tsx— Settings UI with auto-parserwebview-ui/src/components/settings/constants.ts— Azure models in provider mapwebview-ui/src/components/settings/utils/providerModelConfig.ts— Default model configwebview-ui/src/components/settings/ThinkingBudget.tsx— Reasoning effort default fixwebview-ui/src/i18n/locales/en/settings.json— Updated labels/descriptionspackages/types/src/providers/azure.ts— Default model ID